/*
 *  Platform_FileTools.cpp
 *  Fridge
 *
 *  Created by Jeremy Abel on 6/20/12.
 *  Copyright 2012 __MyCompanyName__. All rights reserved.
 *
 */

#include "Main.h"

// My header
#include "FileTools.h"

// Fridge
#include "StringHash.h"
#include "SuperTree.h"

// External
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <ftw.h>
#include <sys/stat.h>
#include <sys/types.h>

// QT
#include <QFileDialog>

static int RemoveFile( const char* szPath, const struct stat *pxStatBuffer, int iFlag, struct FTW *pxFtwBuffer );
static int WalkDirectoryCallback( const char* szPath, const struct stat *pxStatBuffer, int iFlag, struct FTW *pxFtwBuffer );
static void FindInternal( SuperTree<FFileInfo>* pxFileList, const char* szPath, const char* szExtension, const bool bRecursive, const FHandle xParent );
static const char* GetExtensionForType( FRIDGE_FILE_TYPES eType );
static const char* GetDirectoryForType( FRIDGE_FILE_TYPES eType );
static const char* GetFilterForType( FRIDGE_FILE_TYPES eType );
static const char* GetTempDirectoryForType( FRIDGE_TEMP_DIRECTORIES eType );
static const char* GetFileNamesForType( FRIDGE_INTERNAL_FILES eType );

static const char* _name = 0;
static void setName( const char* aName ) { _name = aName; };

void FileTools::Platform_FindFiles ( SuperTree<FFileInfo>* pxFileList, FRIDGE_FILE_TYPES eType, const bool bRecursive )
{	
	char szDir[ PATH_MAX ];
	szDir[0] = 0;
	
	strcpy( szDir, GetDirectoryForType( eType ) );
	strcat( szDir, "//" );
	
	FindInternal( pxFileList, szDir, GetExtensionForType( eType ), bRecursive, TREE_HANDLE_HEAD );
}

bool FileTools::Platform_SavePrompt( FRIDGE_FILE_TYPES eType, char* szSaveFile )
{
    const char* szFileType = GetExtensionForType( eType );

    QString szFileName = 0;

    if( szFileType == "fridge")
    {
        szFileName = QFileDialog::getSaveFileName( 0, "Save Preset", "~", "Fridge Preset Files (*.fridge)");
    }
    else
    {
        szFileName = QFileDialog::getSaveFileName( 0, "Save Template", "~", "Fridge Template Files (*.template)");
    }

    strncpy( szSaveFile, szFileName.toLocal8Bit().data(), FRIDGE_MAX_PATH );

    return true;
}

bool FileTools::Platform_OpenPrompt( FRIDGE_FILE_TYPES eType, char* szOpenFile )
{
    const char* szFileType = GetExtensionForType( eType );

    // Can't open any other type right now
    if( szFileType != "fridge")
    {
        return false;
    }

    QString szFileName = QFileDialog::getOpenFileName( 0, "Open Preset", "~", "Fridge Preset Files (*.fridge)");
    strncpy( szOpenFile, szFileName.toLocal8Bit().data(), FRIDGE_MAX_PATH );

    return true;
}

bool FileTools::Platform_MakeDirectory( const char* szPath )
{
    if( mkdir( szPath, 0777 ) == -1 )
	{
		FridgeLogf( "mkdir error: %s", strerror( errno ) );
		return false;
	}
	
	return true;
}

bool FileTools::Platform_RenameDirectory( const char* szOldDirectory, const char* szNewDirectory )
{
	if( rename( szOldDirectory, szNewDirectory ) == -1 )
	{
		FridgeLogf( "rename error: %s", strerror( errno ) );
		return false;
	}
	
	return true;
}

bool FileTools::Platform_RemoveTempDirectory()
{
	// Report files before directories
	int iFlags = 0;
	iFlags |= FTW_DEPTH;
	
	// Empty fridge temp dir
    if( nftw( GetTempDirectoryForType( FRIDGE_DIR_TEMPORARY_APP ), RemoveFile, 200, iFlags ) == -1 )
	{
		FridgeLogf( "RemoveTempDir error: %s", strerror( errno ) );
		return false;
	}
	
	return true;
}

int RemoveFile( const char* szPath, const struct stat *pxStatBuffer, int iFlag, struct FTW *pxFtwBuffer )
{
	// DON'T CALL THIS FUNCTION MANUALLY. EVER.
	
	remove( szPath );
	return 0;
}

const char* FileTools::Platform_GetTempDirectoryForType( FRIDGE_TEMP_DIRECTORIES eType )
{
    return GetTempDirectoryForType( eType );
}

// ___________________________________________________________________________________________

void FindInternal( SuperTree<FFileInfo>* pxFileList, const char* szPath, const char* szExtension, const bool bRecursive, const FHandle xParent )
{
	if( !pxFileList )
	{
		return;
	}
	
	DIR* d_fh;
	struct dirent* entry;
	char szFullString[ PATH_MAX ];
	strcpy( szFullString, szPath );
	strcat( szFullString, "*.*" );
	
	while( (d_fh = opendir(szPath)) == NULL )
	{
		fprintf(stderr, "Could not open directory: %s\n", szPath);
		exit(-1);
	}
	
	while( (entry = readdir(d_fh)) != NULL )
	{
		if (strncmp(entry->d_name, "..", 2) != 0 &&
			strncmp(entry->d_name, ".",  1) != 0)
		{
			if (entry->d_type == DT_DIR) 
			{
				if (bRecursive) 
				{
					char szSubPath[ PATH_MAX ];
					strcpy( szSubPath, szPath );
					sprintf( szSubPath, "%s%s/", szPath, entry->d_name );
					
					const FHandle xNewDirectoryHandle = pxFileList->Add( new FFileInfo( szSubPath, true ) );
					FindInternal( pxFileList, szSubPath, szExtension, true, xNewDirectoryHandle );
				}
			}
			else
			{
				const u_int uExtensionLength = strlen( szExtension );
				const u_int uNameLength = strlen( entry->d_name );
				const bool  bHasExtension = !strcmp( &( entry->d_name[ uNameLength - uExtensionLength ] ), szExtension );
				
				if (bHasExtension)
				{
					char szFull[ PATH_MAX ];
					sprintf( szFull, "%s%s", szPath, entry->d_name );
					FFileInfo xInfo;
					
					printf("%s\n", szFull);

					pxFileList->Add( new FFileInfo( szFull, false ), xParent );
				}
			}
		}		
	}
	
	// We're done here
	closedir(d_fh);
}

const char* GetFilterForType( FRIDGE_FILE_TYPES eType )
{
	
	static const char* aszFilter[] = {
		"Fridge Presets\0*.fridge\0",      // FRIDGE_FILE_PRESET
		"Fridge Templates\0*.template\0",  // FRIDGE_FILE_TEMPLATE
		"Fridge Resources\0*.png\0",	   // FRIDGE_FILE_RESOURCE // TODO: TEMPORARY ONLY
	};
	
	return aszFilter[ eType ];
	
}

const char* GetExtensionForType( FRIDGE_FILE_TYPES eType )
{
	static const char* aszExtensions[] = {
		"fridge",    // FRIDGE_FILE_PRESET
		"template",  // FRIDGE_FILE_TEMPLATE
		"png",		 // FRIDGE_FILE_RESOURCE // TODO: TEMPORARY ONLY
        "dat",       // FRIDGE_FILE_DATA
	};
	
	return aszExtensions[ eType ];
}

const char* GetDirectoryForType( FRIDGE_FILE_TYPES eType )
{
	
	// TODO: At some point we need to prepend the User directory to this rather than using the directory where fridge is installed
	
	static const char* aszDirectory[] =  {
        "Presets",     // FRIDGE_FILE_PRESET
        "Templates",   // FRIDGE_FILE_TEMPLATE
        "Templates",   // FRIDGE_FILE_RESOURCE // TODO: TEMPORARY ONLY
        "/tmp/fridge", // FRIDGE_FILE_DATA
	};
	
	return aszDirectory[ eType ];
}

const char* GetTempDirectoryForType( FRIDGE_TEMP_DIRECTORIES eType )
{
    static const char* aszDirectory[] = {
        "/tmp",                             // FRIDGE_DIR_TEMPORARY_ROOT
        "/tmp/fridge",                      // FRIDGE_DIR_TEMPORARY_APP
    };

    return aszDirectory[ eType ];
}

//eof
